using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Wrath.RuleForge.Interfaces;
using Wrath.Xml;
using Wrath.Xml.Objects;

namespace Wrath.RuleForge.Imaging
{
    public partial class FrameGuideControl : Control, ICanSave, ICanUndo
    {
        public class Rectangle
        {
            public enum PointPosition
            {
                None,
                TopLeft,
                Top,
                TopRight,
                Left,
                Middle,
                Right,
                BottomLeft,
                Bottom,
                BottomRight
            }

            public int X;
            public int Y;
            public int Width;
            public int Height;

            public int Left { get { return X; } }
            public int Top { get { return Y; } }
            public int Right { get { return X + Width - 1; } }
            public int Bottom { get { return Y + Height - 1; } }

            public Rectangle()
            {
            }

            public Rectangle(int x, int y, int width, int height)
            {
                X = x;
                Y = y;
                Width = width;
                Height = height;
            }

            public bool ContainsPoint(int x, int y)
            {
                if (x < Left || x > Right) return false;
                if (y < Top || y > Bottom) return false;

                return true;
            }

            public PointPosition GetPointPosition(int x, int y, int variance)
            {
                bool onTop = false;
                bool onBottom = false;
                bool onLeft = false;
                bool onRight = false;

                // Right or Left?
                if (x > Right - variance && x < Right + variance &&
                    y >= Top && y <= Bottom)
                {
                    onRight = true;
                }
                else if (x > Left - variance && x < Left + variance &&
                    y >= Top && y <= Bottom)
                {
                    onLeft = true;
                }

                // Bottom or Top?
                if (y > Bottom - variance && y < Bottom + variance &&
                    x >= Left && x <= Right)
                {
                    onBottom = true;
                }
                else if (y > Top - variance && y < Top + variance &&
                    x >= Left && x <= Right)
                {
                    onTop = true;
                }

                if (onBottom)
                {
                    if (onRight) return PointPosition.BottomRight;
                    else if (onLeft) return PointPosition.BottomLeft;
                    else return PointPosition.Bottom;
                }
                else if (onTop)
                {
                    if (onRight) return PointPosition.TopRight;
                    else if (onLeft) return PointPosition.TopLeft;
                    else return PointPosition.Top;
                }
                else if (onLeft) return PointPosition.Left;
                else if (onRight) return PointPosition.Right;
                else if (ContainsPoint(x, y)) return PointPosition.Middle;

                return PointPosition.None;
            }

            internal void CopyTo(ref Rectangle frame)
            {
                frame = new Rectangle();

                frame.X = this.X;
                frame.Y = this.Y;
                frame.Width = this.Width;
                frame.Height = this.Height;
            }
        }        

        private Element sourceElement;
        private string frameName = "";
        [Browsable(false)]
        public string FrameName { get { return frameName; } }

        #region Utily Properties

        private bool lockAreas = true;
        [Category("FrameGuide")]
        public bool LockAreas { get { return lockAreas; } set { lockAreas = value; } }

        private Rectangle selectedRectangle = null;
        private RectangleAreas selectedRectangleArea = RectangleAreas.None;
        [Category("FrameGuide")]
        public RectangleAreas SelectedArea { get { return selectedRectangleArea; } }
        [Category("FrameGuide")]
        public Rectangle SelectedRectangle { get { return selectedRectangle; } }

        private int marginWidth = 20;
        private int marginHeight = 20;

        [Category("FrameGuide")]
        public int DefaultMarginWidth { get { return marginWidth; } set { marginWidth = value; } }
        [Category("FrameGuide")]
        public int DefaultMarginHeight { get { return marginHeight; } set { marginHeight = value; } }

        #endregion

        #region Rectangles

        private Rectangle topLeft = null;

        [Browsable(false)]
        public Rectangle TopLeft
        {
            get { return topLeft; }
            set { CreateUndoData(RectangleAreas.TopLeft, topLeft); topLeft = value; }
        }        

        private Rectangle top = null;

        [Browsable(false)]
        public Rectangle TopRect
        {
            get { return top; }
            set { CreateUndoData(RectangleAreas.Top, top); top = value; }
        }

        private Rectangle topRight = null;

        [Browsable(false)]
        public Rectangle TopRight
        {
            get { return topRight; }
            set { CreateUndoData(RectangleAreas.TopRight, topRight); topRight = value; }
        }

        private Rectangle left = null;

        [Browsable(false)]
        public Rectangle LeftRect
        {
            get { return left; }
            set { CreateUndoData(RectangleAreas.Left, left); left = value; }
        }

        private Rectangle middle = null;

        [Browsable(false)]
        public Rectangle Middle
        {
            get { return middle; }
            set { CreateUndoData(RectangleAreas.Middle, middle); middle = value; }
        }

        private Rectangle right = null;

        [Browsable(false)]
        public Rectangle RightRect
        {
            get { return right; }
            set { CreateUndoData(RectangleAreas.Right, right); right = value; }
        }

        private Rectangle bottomLeft = null;

        [Browsable(false)]
        public Rectangle BottomLeft
        {
            get { return bottomLeft; }
            set { CreateUndoData(RectangleAreas.BottomLeft, bottomLeft); bottomLeft = value; }
        }

        private Rectangle bottom = null;

        [Browsable(false)]
        public Rectangle BottomRect
        {
            get { return bottom; }
            set { CreateUndoData(RectangleAreas.Bottom, bottom); bottom = value; }
        }

        private Rectangle bottomRight = null;

        [Browsable(false)]
        public Rectangle BottomRight
        {
            get { return bottomRight; }
            set { CreateUndoData(RectangleAreas.BottomRight, bottomRight); bottomRight = value; }
        }

        private Rectangle decal = null;

        [Browsable(false)]
        public Rectangle Decal
        {
            get { return decal; }
            set { CreateUndoData(RectangleAreas.Decal, decal); decal = value; }
        }

        #endregion

        #region Constructors

        // Required for VS Designer.
        public FrameGuideControl()
        {
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            InitializeComponent();

            undoData.Clear();

            this.BackgroundImage = new Bitmap(200, 200);

            this.Width = this.BackgroundImage.Width + 1;
            this.Height = this.BackgroundImage.Height + 1;
        }

        public FrameGuideControl(Element source)
        {
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            InitializeComponent();            

            ParseElement(source);
        }

        public void ParseElement(Element sourceElement)
        {
            undoData.Clear();

            this.sourceElement = sourceElement;

            Wrath.Xml.Attribute nameAttribute = sourceElement.FindAttribute("name");
            if (nameAttribute != null)
            {
                frameName = nameAttribute.Value.Substring(0);
            }

            Element element = sourceElement.FindFirstElement("bitmap", false);
            if (element != null)
            {
                Wrath.Xml.Attribute fileAttribute = element.FindAttribute("file");
                if (fileAttribute != null)
                {
                    string relativePath = fileAttribute.Value.Substring(0);
                    string fullPath = Globals.FantasyGroundsPaths.CurrentRuleSet + "\\" + relativePath.Replace('/', '\\');
                    this.BackgroundImage = Image.FromFile(fullPath);
                    this.Width = this.BackgroundImage.Width + 1;
                    this.Height = this.BackgroundImage.Height + 1;
                }
            }

            element = sourceElement.FindFirstElement("topleft", false);
            if (element != null)
            {
                topLeft = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("top", false);
            if (element != null)
            {
                top = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("topright", false);
            if (element != null)
            {
                topRight = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("left", false);
            if (element != null)
            {
                left = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("middle", false);
            if (element != null)
            {
                middle = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("right", false);
            if (element != null)
            {
                right = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("bottomleft", false);
            if (element != null)
            {
                bottomLeft = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("bottom", false);
            if (element != null)
            {
                bottom = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("bottomright", false);
            if (element != null)
            {
                bottomRight = ParseRectangle(element);
            }

            element = sourceElement.FindFirstElement("decal", false);
            if (element != null)
            {
                decal = ParseRectangle(element);
            }
        }

        private Rectangle ParseRectangle(Element element)
        {
            Rectangle rectangle = null;

            Wrath.Xml.Attribute attribute = element.FindAttribute("rect");
            if (attribute != null)
            {
                string rect = attribute.Value;
                string[] bounds = rect.Split(',');

                if (bounds.Length == 4)
                {
                    rectangle = new Rectangle();
                    rectangle.X = Convert.ToInt32(bounds[0]);
                    rectangle.Y = Convert.ToInt32(bounds[1]);
                    rectangle.Width = Convert.ToInt32(bounds[2]);
                    rectangle.Height = Convert.ToInt32(bounds[3]);
                }
            }

            return rectangle;
        }

        #endregion

        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            if (this.BackgroundImage != null)
            {
                g.DrawImageUnscaled(this.BackgroundImage, new Point(0, 0));
            }

            Color transparentGray = Color.FromArgb(96, Color.CornflowerBlue);
            Brush outerBrush = new SolidBrush(transparentGray);

            Pen outerPen = new Pen(Color.Black);
            outerPen.Width = 1;
            outerPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;            

            // topLeft
            if (topLeft != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(topLeft.X, topLeft.Y, topLeft.Width, topLeft.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawRectangle(outerPen, rect);
            }

            // top
            if (top != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(top.X, top.Y, top.Width, top.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawLine(outerPen, rect.Left, rect.Top, rect.Right, rect.Top);
            }


            // topRight
            if (topRight != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(topRight.X, topRight.Y, topRight.Width, topRight.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawRectangle(outerPen, rect);
            }

            // left
            if (left != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(left.X, left.Y, left.Width, left.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawLine(outerPen, rect.Left, rect.Top, rect.Left, rect.Bottom);
            }

            // right
            if (right != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(right.X, right.Y, right.Width, right.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawLine(outerPen, rect.Right, rect.Top, rect.Right, rect.Bottom);
            }

            // bottomLeft
            if (bottomLeft != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(bottomLeft.X, bottomLeft.Y, bottomLeft.Width, bottomLeft.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawRectangle(outerPen, rect);
            }

            // bottom
            if (bottom != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(bottom.X, bottom.Y, bottom.Width, bottom.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawLine(outerPen, rect.Left, rect.Bottom, rect.Right, rect.Bottom);
            }

            // bottomRight
            if (bottomRight != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(bottomRight.X, bottomRight.Y, bottomRight.Width, bottomRight.Height);

                g.FillRectangle(outerBrush, rect);
                g.DrawRectangle(outerPen, rect);
            }

            // decal
            if (decal != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(decal.X, decal.Y, decal.Width, decal.Height);

                Pen decalPen = new Pen(Color.DarkGreen);
                decalPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

                g.FillRectangle(outerBrush, rect);
                g.DrawRectangle(decalPen, rect);
            }

            // middle
            if (middle != null)
            {
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(middle.X, middle.Y, middle.Width, middle.Height);

                Pen middlePen = new Pen(Color.Black);
                middlePen.Width = 1;

                g.DrawRectangle(middlePen, rect);
            }

            if (dragNewFrame)
            {
                System.Drawing.Rectangle newRect = new System.Drawing.Rectangle(mouseDownPoint, new System.Drawing.Size(mouseX - mouseDownPoint.X, mouseY - mouseDownPoint.Y));
                Pen newPen = new Pen(Color.Green);
                newPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

                g.DrawRectangle(newPen, newRect);
            }
            else
            {
                if (selectedRectangle != null)
                {
                    Pen selectedPen = new Pen(Color.Red);
                    selectedPen.Width = 1;
                    selectedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

                    System.Drawing.Rectangle rect = new System.Drawing.Rectangle(selectedRectangle.X, selectedRectangle.Y, selectedRectangle.Width, selectedRectangle.Height);
                    g.DrawRectangle(selectedPen, rect);
                }
            }
        }

        #region New Frame Functions

        public event EventHandler NewFrameComplete;

        private bool dragNewFrame = false;        

        [Browsable(false)]
        public bool DragNew
        {
            get { return dragNewFrame; }
            set { dragNewFrame = value; }
        }

        private void InitNewFrame()
        {
            CreateUndoData(selectedRectangleArea, selectedRectangle);
            NullifyRectangle(selectedRectangleArea);
        }

        private void NullifyRectangle(RectangleAreas selectedRectangleArea)
        {
            selectedRectangle = null;

            if (selectedRectangleArea == RectangleAreas.TopLeft) TopLeft = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Top) TopRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.TopRight) TopRight = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Left) LeftRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Middle) Middle = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Right) RightRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.BottomLeft) BottomLeft = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Bottom) BottomRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.BottomRight) BottomRight = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Decal) Decal = selectedRectangle;
        }

        private void CreateNewFrame(Point newFrameStart, Point newFrameEnd)
        {
            if (selectedRectangleArea == RectangleAreas.None) return;

            selectedRectangle = GetNewFrameRectangle(newFrameStart, newFrameEnd);

            if (selectedRectangleArea == RectangleAreas.TopLeft) TopLeft = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Top) TopRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.TopRight) TopRight = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Left) LeftRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Middle) Middle = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Right) RightRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.BottomLeft) BottomLeft = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Bottom) BottomRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.BottomRight) BottomRight = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Decal) Decal = selectedRectangle;

            this.Refresh();

            if (SelectedAreaModified != null)
                SelectedAreaModified(this, new EventArgs());

            if (NewFrameComplete != null)
                NewFrameComplete(this, new EventArgs());
        }

        private Rectangle GetNewFrameRectangle(Point newFrameStart, Point newFrameEnd)
        {
            Rectangle newRectangle = new Rectangle();

            if (newFrameStart.X < newFrameEnd.X)
            {
                newRectangle.X = newFrameStart.X;
                newRectangle.Width = newFrameEnd.X - newFrameStart.X;
            }
            else
            {
                newRectangle.X = newFrameEnd.X;
                newRectangle.Width = newFrameStart.X - newFrameEnd.X;
            }

            if (newFrameStart.Y < newFrameEnd.Y)
            {
                newRectangle.Y = newFrameStart.Y;
                newRectangle.Height = newFrameEnd.Y - newFrameStart.Y;
            }
            else
            {
                newRectangle.Y = newFrameEnd.Y;
                newRectangle.Height = newFrameStart.Y - newFrameEnd.Y;
            }

            return newRectangle;
        }

        public void CreateNewFrame(Rectangle newFrame)
        {
            if (newFrame == null) return;

            newFrame.CopyTo(ref selectedRectangle);

            if (selectedRectangleArea == RectangleAreas.TopLeft) TopLeft = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Top) TopRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.TopRight) TopRight = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Left) LeftRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Middle) Middle = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Right) RightRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.BottomLeft) BottomLeft = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Bottom) BottomRect = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.BottomRight) BottomRight = selectedRectangle;
            else if (selectedRectangleArea == RectangleAreas.Decal) Decal = selectedRectangle;

            this.Refresh();

            if (SelectedAreaModified != null)
                SelectedAreaModified(this, new EventArgs());
        }


        internal void DeleteSelectedArea()
        {
            if (selectedRectangleArea == RectangleAreas.TopLeft) TopLeft = null;
            else if (selectedRectangleArea == RectangleAreas.Top) TopRect = null;
            else if (selectedRectangleArea == RectangleAreas.TopRight) TopRight = null;
            else if (selectedRectangleArea == RectangleAreas.Left) LeftRect = null;
            else if (selectedRectangleArea == RectangleAreas.Middle) Middle = null;
            else if (selectedRectangleArea == RectangleAreas.Right) RightRect = null;
            else if (selectedRectangleArea == RectangleAreas.BottomLeft) BottomLeft = null;
            else if (selectedRectangleArea == RectangleAreas.Bottom) BottomRect = null;
            else if (selectedRectangleArea == RectangleAreas.BottomRight) BottomRight = null;
            else if (selectedRectangleArea == RectangleAreas.Decal) Decal = null;

            SelectArea(RectangleAreas.None);
        }

        #endregion

        #region Mouse Handlers

        private bool mouseDown = false;

        private const int borderVariance = 2;

        private int mouseX = 0;
        private int mouseY = 0;
        private Point mouseDownPoint;
        private Point mouseUpPoint;       

        private Rectangle.PointPosition dragDirection;

        public event EventHandler SelectedAreaModified;

        private void FrameGuideControl_MouseDown(object sender, MouseEventArgs e)
        {
            mouseDown = true;
            mouseDownPoint = new Point(e.X, e.Y);

            if (dragNewFrame)
            {
                InitNewFrame();
            }
            else if (dragDirection == Rectangle.PointPosition.Middle)
            {
                InitMoveFrame();
            }
            else if (dragDirection != Rectangle.PointPosition.None)
            {
                InitSizeFrame();
            }
        }

        private void InitSizeFrame()
        {
            CreateUndoData(selectedRectangleArea, selectedRectangle);
        }

        private void InitMoveFrame()
        {
            CreateUndoData(selectedRectangleArea, selectedRectangle);
        }

        private void FrameGuideControl_MouseUp(object sender, MouseEventArgs e)
        {
            mouseDown = false;
            mouseUpPoint = new Point(e.X, e.Y);

            if (dragNewFrame)
            {
                dragNewFrame = false;
                
                CreateNewFrame(mouseDownPoint, mouseUpPoint);
            }
        }

        private void FrameGuideControl_MouseMove(object sender, MouseEventArgs e)
        {
            mouseX = e.X;
            mouseY = e.Y;

            if (mouseDown)
            {
                if (SelectedArea == RectangleAreas.None) return;

                if (!dragNewFrame && dragDirection != Rectangle.PointPosition.None)
                {
                    int xDiff = mouseX - mouseDownPoint.X;
                    int yDiff = mouseY - mouseDownPoint.Y;

                    if (dragDirection == Rectangle.PointPosition.Middle)
                    {
                        // Check Lock

                        // Just Move
                        selectedRectangle.X += xDiff;
                        selectedRectangle.Y += yDiff;
                    }
                    else
                    {
                        if (dragDirection == Rectangle.PointPosition.TopLeft ||
                            dragDirection == Rectangle.PointPosition.Top ||
                            dragDirection == Rectangle.PointPosition.TopRight)
                        {
                            selectedRectangle.Y += yDiff;
                            selectedRectangle.Height -= yDiff;
                        }

                        if (dragDirection == Rectangle.PointPosition.TopLeft ||
                            dragDirection == Rectangle.PointPosition.Left ||
                            dragDirection == Rectangle.PointPosition.BottomLeft)
                        {
                            selectedRectangle.X += xDiff;
                            selectedRectangle.Width -= xDiff;
                        }

                        if (dragDirection == Rectangle.PointPosition.BottomRight ||
                            dragDirection == Rectangle.PointPosition.Right)
                        {
                            selectedRectangle.Width += xDiff;
                        }

                        if (dragDirection == Rectangle.PointPosition.BottomRight ||
                            dragDirection == Rectangle.PointPosition.Bottom)
                        {
                            selectedRectangle.Height += yDiff;
                        }
                    }

                    mouseDownPoint.X = mouseX;
                    mouseDownPoint.Y = mouseY;

                    // Redraw!
                    this.Refresh();
                }
                else
                {
                    // Redraw!
                    this.Refresh();
                }

                if (SelectedAreaModified != null)
                    SelectedAreaModified(this, new EventArgs());
            }
            else
            {
                // Check if we're over the selected rectangle or its edges.
                if (selectedRectangle != null)
                {
                    Rectangle.PointPosition oldPosition = dragDirection;
                    dragDirection = selectedRectangle.GetPointPosition(mouseX, mouseY, borderVariance);
                    if (dragDirection != oldPosition)
                        SetMouseCursor(dragDirection);
                }
                else
                {
                    dragDirection = Rectangle.PointPosition.None;
                    this.Cursor = Cursors.Default;
                }                
            }
        }

        private void SetMouseCursor(Rectangle.PointPosition pointPosition)
        {
            switch (pointPosition)
            {
                case Rectangle.PointPosition.TopLeft:
                    this.Cursor = Cursors.SizeNWSE;
                    break;
                case Rectangle.PointPosition.BottomRight:
                    this.Cursor = Cursors.SizeNWSE;
                    break;

                case Rectangle.PointPosition.Top:
                    this.Cursor = Cursors.SizeNS;
                    break;
                case Rectangle.PointPosition.Bottom:
                    this.Cursor = Cursors.SizeNS;
                    break;

                case Rectangle.PointPosition.TopRight:
                    this.Cursor = Cursors.SizeNESW;
                    break;
                case Rectangle.PointPosition.BottomLeft:
                    this.Cursor = Cursors.SizeNESW;
                    break;

                case Rectangle.PointPosition.Left:
                    this.Cursor = Cursors.SizeWE;
                    break;
                case Rectangle.PointPosition.Right:
                    this.Cursor = Cursors.SizeWE;
                    break;

                case Rectangle.PointPosition.Middle:
                    this.Cursor = Cursors.SizeAll;
                    break;

                default:
                    this.Cursor = Cursors.Default;
                    break;
            }
        }        

        #endregion

        #region Utility Methods

        private Rectangle GetRectangleAtPoint(int x, int y)
        {
            // check each rectangle
            if (selectedRectangleArea != RectangleAreas.TopLeft && PointIsInRectangle(x, y, topLeft)) return topLeft;
            else if (selectedRectangleArea != RectangleAreas.Top && PointIsInRectangle(x, y, top)) return top;
            else if (selectedRectangleArea != RectangleAreas.TopRight && PointIsInRectangle(x, y, topRight)) return topRight;

            else if (selectedRectangleArea != RectangleAreas.Left && PointIsInRectangle(x, y, left)) return left;
            else if (selectedRectangleArea != RectangleAreas.Middle && PointIsInRectangle(x, y, middle)) return middle;
            else if (selectedRectangleArea != RectangleAreas.Right && PointIsInRectangle(x, y, right)) return right;

            else if (selectedRectangleArea != RectangleAreas.BottomLeft && PointIsInRectangle(x, y, bottomLeft)) return bottomLeft;
            else if (selectedRectangleArea != RectangleAreas.Bottom && PointIsInRectangle(x, y, bottom)) return bottom;
            else if (selectedRectangleArea != RectangleAreas.BottomRight && PointIsInRectangle(x, y, bottomRight)) return bottomRight;

            else if (selectedRectangleArea != RectangleAreas.Decal && PointIsInRectangle(x, y, decal)) return decal;

            return null;
        }

        private bool PointIsInRectangle(int x, int y, Rectangle rectangle)
        {
            if (rectangle == null) return false;
            return rectangle.ContainsPoint(x, y);
        }

        private Rectangle GetRectangleFromArea(RectangleAreas rectangleArea)
        {
            // check each area
            if (rectangleArea == RectangleAreas.TopLeft) return topLeft;
            else if (rectangleArea == RectangleAreas.Top) return top;
            else if (rectangleArea == RectangleAreas.TopRight) return topRight;

            else if (rectangleArea == RectangleAreas.Left) return left;
            else if (rectangleArea == RectangleAreas.Middle) return middle;
            else if (rectangleArea == RectangleAreas.Right) return right;

            else if (rectangleArea == RectangleAreas.BottomLeft) return bottomLeft;
            else if (rectangleArea == RectangleAreas.Bottom) return bottom;
            else if (rectangleArea == RectangleAreas.BottomRight) return bottomRight;

            else if (rectangleArea == RectangleAreas.Decal) return decal;

            return null;
        }

        private RectangleAreas GetAreaFromRectangle(Rectangle rectangle)
        {
            
            if (object.Equals(rectangle,topLeft)) return RectangleAreas.TopLeft;
            else if (object.Equals(rectangle,top)) return RectangleAreas.Top;
            else if (object.Equals(rectangle,topRight)) return RectangleAreas.TopRight;
            else if (object.Equals(rectangle,left)) return RectangleAreas.Left;
            else if (object.Equals(rectangle,middle)) return RectangleAreas.Middle;
            else if (object.Equals(rectangle,right)) return RectangleAreas.Right;
            else if (object.Equals(rectangle,bottomLeft)) return RectangleAreas.BottomLeft;
            else if (object.Equals(rectangle,bottom)) return RectangleAreas.Bottom;
            else if (object.Equals(rectangle,bottomRight)) return RectangleAreas.BottomRight;
            else if (object.Equals(rectangle,decal)) return RectangleAreas.Decal;

            return RectangleAreas.None;
        }        

        #endregion

        #region Selection Methods

        public event EventHandler SelectedAreaChanged;

        private void FrameGuideControl_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Rectangle rectangle = GetRectangleAtPoint(e.X, e.Y);

            SelectRectangle(rectangle);
        }

        /// <summary>
        /// Selects a specific rectangle and causes control to redraw.
        /// </summary>
        /// <param name="rectangle"></param>
        private void SelectRectangle(Rectangle rectangle)
        {
            System.Drawing.Rectangle systemRectangle = System.Drawing.Rectangle.Empty;

            Region invalidatedRegion = new Region();
            if (selectedRectangle != null)
            {
                systemRectangle = new System.Drawing.Rectangle(selectedRectangle.X, selectedRectangle.Y, selectedRectangle.Width, selectedRectangle.Height);
                invalidatedRegion.Union(systemRectangle);
            }

            selectedRectangle = rectangle;
            selectedRectangleArea = (rectangle == null) ? RectangleAreas.None : GetAreaFromRectangle(rectangle);

            if (selectedRectangle != null)
            {
                systemRectangle = new System.Drawing.Rectangle(selectedRectangle.X, selectedRectangle.Y, selectedRectangle.Width, selectedRectangle.Height);
                invalidatedRegion.Union(systemRectangle);

                if (SelectedAreaChanged != null)
                    SelectedAreaChanged(this, new EventArgs());
            }

            this.Invalidate(invalidatedRegion);
            //this.Refresh();
        }

        public void SelectArea(RectangleAreas area)
        {
            Rectangle rectangle = GetRectangleFromArea(area);
            SelectRectangle(rectangle);
            selectedRectangleArea = area;
        }

        private bool IsSelected(Rectangle rectangle)
        {
            if (rectangle != null && rectangle == selectedRectangle) return true;

            return false;
        }       

        #endregion

        #region ICanUndo Members

        private bool enableUndo = false;
        private Stack<RectangleUndoData> undoData = new Stack<RectangleUndoData>(0);

        [Browsable(false)]
        public bool UndoEnabled
        {
            get { return enableUndo; }
        }

        public void Undo()
        {
            SelectArea(RectangleAreas.None);

            // Do some undo stuffs.
            if (undoData.Count > 0)
            {
                RectangleUndoData data = undoData.Pop();

                switch (data.Area)
                {
                    case RectangleAreas.Bottom: bottom = data.RectangleData; break;
                    case RectangleAreas.BottomLeft: bottomLeft = data.RectangleData; break;
                    case RectangleAreas.BottomRight: bottomRight = data.RectangleData; break;
                    case RectangleAreas.Decal: decal = data.RectangleData; break;
                    case RectangleAreas.Left: left = data.RectangleData; break;
                    case RectangleAreas.Middle: middle = data.RectangleData; break;
                    case RectangleAreas.Right: right = data.RectangleData; break;
                    case RectangleAreas.Top: top = data.RectangleData; break;
                    case RectangleAreas.TopLeft: topLeft = data.RectangleData; break;
                    case RectangleAreas.TopRight: topRight = data.RectangleData; break;
                }

                if (NewFrameComplete != null)
                {
                    NewFrameComplete(this, new EventArgs());
                }
            }

            if (undoData.Count < 1)
            {
                enableUndo = false;
            }

            this.Refresh();
        }

        private void CreateUndoData(RectangleAreas area, Rectangle rectangle)
        {
            Rectangle rectangleCopy = null;
            if (rectangle != null)
            {
                rectangleCopy = new Rectangle();
                rectangle.CopyTo(ref rectangleCopy);
            }

            RectangleUndoData data = new RectangleUndoData(area, rectangleCopy);

            // Add this rectangle to the undo stack.
            undoData.Push(data);

            enableUndo = true;
            modified = true;
        }

        #endregion

        #region ICanSave Members

        private bool modified;

        [Browsable(false)]
        public bool Modified
        {
            get { return modified; }
        }

        public void Save()
        {
            if (modified)
            {
                // Transfer rectangle dimensions to the underlying element.
                if (topLeft != null)
                {
                    Element element = sourceElement.FindFirstElement("topleft", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, topLeft);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(topLeft, "topleft"));
                    } 
                }

                if (top != null)
                {
                    Element element = sourceElement.FindFirstElement("top", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, top);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(top, "top"));
                    } 
                }

                if (topRight != null)
                {
                    Element element = sourceElement.FindFirstElement("topright", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, topRight);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(topRight, "topright"));
                    } 
                }

                if (left != null)
                {
                    Element element = sourceElement.FindFirstElement("left", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, left);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(left, "left"));
                    } 
                }

                if (middle != null)
                {
                    Element element = sourceElement.FindFirstElement("middle", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, middle);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(middle, "middle"));
                    } 
                }

                if (right != null)
                {
                    Element element = sourceElement.FindFirstElement("right", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, right);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(right, "right"));
                    } 
                }

                if (bottomLeft != null)
                {
                    Element element = sourceElement.FindFirstElement("bottomleft", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, bottomLeft);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(bottomLeft, "bottomleft"));
                    }
                }

                if (bottom != null)
                {
                    Element element = sourceElement.FindFirstElement("bottom", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, bottom);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(bottom, "bottom"));
                    }
                }

                if (bottomRight != null)
                {
                    Element element = sourceElement.FindFirstElement("bottomright", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, bottomRight);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(bottomRight, "bottomright"));
                    }
                }

                if (decal != null)
                {
                    Element element = sourceElement.FindFirstElement("decal", false);
                    if (element != null)
                    {
                        SetElementRectangle(element, decal);
                    }
                    else
                    {
                        sourceElement.ChildElements.Add(CreateElementFromRectangle(decal, "decal"));
                    }
                }

                EventBroker.FireElementPropertyChanged(sourceElement);

                modified = false;
                undoData.Clear();
                undoData.TrimExcess();
            }
        }

        private Element CreateElementFromRectangle(Rectangle rectangle, string elementName)
        {
            Element element = new Element();
            element.Name = elementName;

            Wrath.Xml.Attribute rect = new Wrath.Xml.Attribute();
            rect.Name = "rect";
            rect.Value = rectangle.X + "," + rectangle.Y + "," + rectangle.Width + "," + rectangle.Height;

            element.Attributes.Add(rect);

            return element;
        }

        private void SetElementRectangle(Element element, Rectangle rectangle)
        {
            Wrath.Xml.Attribute rect = element.FindAttribute("rect");
            if (rect != null)
            {
                rect.Value = rectangle.X + "," + rectangle.Y + "," + rectangle.Width + "," + rectangle.Height;
            }
        }

        #endregion

        private void FrameGuideControl_BackgroundImageChanged(object sender, EventArgs e)
        {
            this.Width = this.BackgroundImage.Width + 1;
            this.Height = this.BackgroundImage.Height + 1;
        }

        private void FrameGuideControl_BackgroundImageLayoutChanged(object sender, EventArgs e)
        {
            this.BackgroundImageLayout = ImageLayout.None;
        }

        private void FrameGuideControl_Resize(object sender, EventArgs e)
        {
            this.Width = this.BackgroundImage.Width + 1;
            this.Height = this.BackgroundImage.Height + 1;
        }
    }
}
